﻿using System;
using System.IO;
using System.Web;
using System.Web.Caching;
using System.Reflection;

namespace Titan.Reflection
{
    public static class FastReflection
    {
        private static object _lock = new object();

        private static Assembly LoadAssemblyNonLock(string assemblyFullPath)
        {
            string key = "CachedAssembly://" + assemblyFullPath;
            Cache cache = HttpRuntime.Cache;

            object assembly = cache.Get(key);

            if (assembly == null)
            {
                lock (_lock)
                {
                    assembly = cache.Get(key);
                    if (assembly == null)
                    {
                        string pdbFile = Path.ChangeExtension(assemblyFullPath, ".pdb");
                        byte[] rawAssembly = File.ReadAllBytes(assemblyFullPath);
                        byte[] rawSymbol = null;
                        CacheDependency dependency = null;

                        if (File.Exists(pdbFile))
                        {
                            rawSymbol = File.ReadAllBytes(pdbFile);
                            dependency = new CacheDependency(new string[] { assemblyFullPath, pdbFile });
                        }
                        else
                        {
                            dependency = new CacheDependency(assemblyFullPath);
                        }
                        assembly = Assembly.Load(rawAssembly, rawSymbol);
                        cache.Insert(key, assembly, dependency);
                    }
                }
            }

            return (Assembly)assembly;
        }

        public static object CreateObject(Type type)
        {
            if (null == type)
                throw new ArgumentNullException("type");
            return ObjectConstructor.Create(type, null);
        }

        public static object CreateObject(Type type, object[] args)
        {
            if (null == type)
                throw new ArgumentNullException("type");
            return ObjectConstructor.Create(type, args);
        }

        public static object InvokeMethod(MethodInfo method, object target, object[] args)
        {
            if (null == method)
                throw new ArgumentNullException("method");
            return ObjectInvoker.Invoke(method, target, args);
        }

        public static object GetProperty(PropertyInfo property, object target)
        {
            if (null == property)
                throw new ArgumentNullException("property");
            MethodInfo method = property.GetGetMethod();
            return ObjectInvoker.Invoke(method, target, null);
        }

        public static void SetProperty(PropertyInfo property, object target, object value)
        {
            if (null == property)
                throw new ArgumentNullException("property");
            MethodInfo method = property.GetSetMethod();
            ObjectInvoker.Invoke(method, target, new object[] { value });
        }

        public static object GetIndexer(PropertyInfo property, object target, object[] index)
        {
            if (null == property)
                throw new ArgumentNullException("property");
            MethodInfo method = property.GetGetMethod();
            return ObjectInvoker.Invoke(method, target, index);
        }

        public static void SetIndexer(PropertyInfo property, object target, object[] index, object value)
        {
            if (null == property)
                throw new ArgumentNullException("property");
            MethodInfo method = property.GetSetMethod();
            ObjectInvoker.Invoke(method, target, new object[] { index, value });
        }

        public static object GetField(FieldInfo field, object target)
        {
            if (null == field)
                throw new ArgumentNullException("field");
            return field.GetValue(target);
        }

        public static void SetField(FieldInfo field, object target, object value)
        {
            if (null == field)
                throw new ArgumentNullException("field");
            field.SetValue(target, value);
        }
    }
}
